#ifndef EDGE_BASE_HPP
#define EDGE_BASE_HPP

#include "meshTypes_fwd.hpp"
#include "crtpBase.hpp"

namespace hms
{

template<typename EdgeType>
class EdgeTraits{};

/**
 * @brief Common interface for edge types
 * 
 * @tparam EdgeType The edge class implementing the interface
 * 
 * Edge types are thin wrapper classes which provide access to edge related
 * functionality,such as length(), normal() and the adjacent cells().
 * 
 * @see CellBase, MeshBase, Edge, StructEdgeBase
 */
template<typename EdgeType>
class EdgeBase : public CrtpBase<EdgeType>
{
public:
	using Traits = EdgeTraits<EdgeType>;
	using MeshType      = typename Traits::MeshType;
	using CellIndices   = typename Traits::CellIndices;
	using VertexIndices = typename Traits::VertexIndices;
	using NormalVector  = typename Traits::NormalVector;

public:
	/**
	 * @brief Returns the parent mesh to which the edge belongs.
	 * 
	 * @return const MeshType&, which has the type specified with
	 * EdgeTraits<EdgeType>::MeshType (e.g. StructMesh for StructEdge).
	 */
	auto mesh() const -> const MeshType&;

	/**
	 * @brief Returns the index of the edge within the parent mesh.
	 * 
	 * @return Index 
	 */
	auto index() const -> Index;

	/**
	 * @brief Returns the length of the edge, i.e. the distance between its
	 * vertices().
	 * 
	 * @return scalar 
	 */
	auto length() const -> scalar;

	/**
	 * @brief Returns the indices of the two vertices (=endpoints) which
	 * form the edge. Using either index as argument to MeshBase::vertex()
	 * returns the vertex' coordinates.
	 * 
	 * @return VertexIndices 
	 */
	auto vertices() const -> VertexIndices;

	/**
	 * @brief Returns the unit normal vector of the edge. It is always oriented
	 * to point from the adjacent cell with a lower index to that with a higher
	 * index.
	 * 
	 * @return NormalVector 
	 */
	auto normal() const -> NormalVector;

	/**
	 * @brief Returns the midpoint of the edge, which lies on the edge at an
	 * equal distance to either of its vertices.
	 * 
	 * @return Vector2s 
	 */
	auto midpoint() const -> Vector2s;

	/**
	 * @brief Returns the intersection point of the two adjacent cells'
	 * centroids with the edge
	 * 
	 * @return Vector2s 
	 */
	auto intersection() const -> Vector2s;

	/**
	 * @brief Returns true if the edge is a boundary edge and false otherwise.
	 * 
	 * @return true 
	 * @return false 
	 */
	auto isBoundary() const -> bool;

	/**
	 * @brief Returns the indices of cells adjacent to the edge.
	 * 
	 * @param order The adjacency order. A value of 1 corresponds to the cells
	 * directly attached to the edge, and any higher value corresponds to cells
	 * neighbouring those of the next lower order.
	 * This parameter currently only has an effect when using structured mesh
	 * types (StructMesh, RectMesh and UniMesh).
	 * @return CellIndices Contains two cell indices. When order=1, these
	 * are always in ascending order. This convention exists to ensure a
	 * consistent orientation of the normal() vector, which always points
	 * from the directly adjacent cell with a lower index to the directly
	 * adjacent cell with a higher index.
	 * When order>1, the cell indices still conform to the normal vector
	 * convention. In a rectangular grid, the normal vector always points from
	 * the cell with the index stored at position 0 to that with the index
	 * stored at position 1.
	 */
	auto cells(Index order=1) const -> CellIndices;

	/**
	 * @brief When called on a boundary edge, returns the index of the domain 
	 * cell of the specified adjacency order.
	 * 
	 * @param order see cells()
	 * @return Index 
	 */
	auto domainCell(Index order=1) const -> Index;
	
	/**
	 * @brief When called on a boundary edge, returns the index of the ghost
	 * cell of the specified adjacency order.
	 * 
	 * @param order see cells()
	 * @return Index 
	 */
	auto ghostCell(Index order=1) const -> Index;

/* Base class must not be instantiated by itself, but only by derived types */
protected:	
	EdgeBase();
};

} // end namespace hms

#endif // EDGE_BASE_H
